package edu.xidian.onlinedocument.controller;

import com.alibaba.fastjson.JSONObject;
import edu.xidian.onlinedocument.entity.DocumentData;
import edu.xidian.onlinedocument.handler.AttributeName;
import edu.xidian.onlinedocument.handler.excpetions.CustomExceptionHandler;
import edu.xidian.onlinedocument.handler.excpetions.custom.CustomException;
import edu.xidian.onlinedocument.handler.interceptor.TokenCheck;
import edu.xidian.onlinedocument.service.OperationService;
import edu.xidian.onlinedocument.service.UserService;
import edu.xidian.onlinedocument.utils.ot.TextManagement;
import edu.xidian.onlinedocument.utils.ot.json.ToJSON;
import edu.xidian.onlinedocument.views.*;
import edu.xidian.onlinedocument.views.groups.*;
import edu.xidian.onlinedocument.service.DocumentService;

import com.fasterxml.jackson.annotation.JsonInclude;
import edu.xidian.onlinedocument.websocket.EditorSocketManager;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;

import java.util.List;

@RestControllerAdvice
@RestController
@RequestMapping("/api/documents")
@JsonInclude(JsonInclude.Include.NON_NULL)
@CrossOrigin(originPatterns = "*",
        methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.DELETE, RequestMethod.PUT}) // 跨域解决方案
public class DocumentController {
    private final UserService userService;

    private final DocumentService documentService;

    private final OperationService operationService;

    @Autowired
    public DocumentController(DocumentService documentService,
                              OperationService operationService,
                              UserService userService) {
        this.documentService = documentService;
        this.operationService = operationService;
        this.userService = userService;
    }

    /**
     * 获取全部文档信息（测试）
     * @return Result
     */
    @GetMapping(path = "/test")
    public Result getAllDocuments() {
        List<DocumentData> documentData = documentService.getAllDocuments();
        return new CustomExceptionHandler<>().sendSuccessResponse(documentData);
    }

    /**
     * 获取文档的基本信息以及创建者信息
     * @param docId 文档id
     * @return Result
     */
    @GetMapping(path = "/{docId}/creators")
    @TokenCheck
    public Result getOneDocumentAndCreatorInfoByDocId(@PathVariable("docId") Integer docId) {
        DocumentData documentData = documentService.getDocumentDataAndCreatorInfoByDocId(docId);
        return new CustomExceptionHandler<>().sendSuccessResponse(documentData);
    }

    /**
     * 获取文档的基本信息以及编辑者信息
     * @param docId 文档id
     * @return Result
     */
    @GetMapping(path = "/{docId}/editors")
    @TokenCheck
    public Result getOneDocumentAndEditorByDocId(@PathVariable("docId") Integer docId) {
        DocumentData documentData = documentService.getDocumentDataAndEditorInfoByDocId(docId);
        return new CustomExceptionHandler<>().sendSuccessResponse(documentData);
    }

    /**
     * 获取文档的基本信息以及创建者信息和编辑者信息
     * @param docId 文档id
     * @return Result
     */
    @GetMapping(path = "/{docId}/creators-and-editors")
    @TokenCheck
    public Result getOneDocumentByDocId(@PathVariable("docId") Integer docId) {
        DocumentData documentData = documentService.getDocumentDataAndCreatorInfoAndEditorInfoByDocId(docId);
        return new CustomExceptionHandler<>().sendSuccessResponse(documentData);
    }

    /**
     * 增加一个新文档
     * request的data字段的属性名为"docName"和"userNum"
     * @param request 文档名和
     * @return Result
     */
    @Transactional
    @Modifying
    @PostMapping(path = "", produces = "application/json")
    @TokenCheck
    public Result addNewDocument(@RequestBody @Validated({DataGroupB.class}) DocumentData documentData,
                                 HttpServletRequest request) {
        // DocumentData documentData = JSON.parseObject(JSON.toJSONString(dataRequest.getData()), DocumentData.class);
        if(StringUtils.isBlank(documentData.getDocName())){
            throw new CustomException(ResultCode.DOCUMENT_NAME_IS_NULL);
        }
        Integer userId = (int) request.getAttribute(AttributeName.CLIENT_ID);
        int docId = documentService.saveOneDocument(documentData.getDocName(), userId);
        return new CustomExceptionHandler<>().sendSuccessResponse(docId);
    }

    /**
     * 创建者删除一个文档
     * @param docId
     * @return Result
     */
    @Transactional
    @Modifying
    @DeleteMapping(path = "/{docId}/creators", produces = "application/json")
    @TokenCheck
    public Result deleteOneDocumentSoft(@PathVariable("docId") Integer docId) {
        documentService.deleteOneDocumentSoft(docId);
        return new CustomExceptionHandler<>().sendSuccessResponse(docId);
    }

    /**
     * 从数据库中删除一个文档，无法恢复
     * @param docId
     * @return Result
     */
    @Transactional
    @Modifying
    @DeleteMapping(path = "/{docId}/creators/flag", produces = "application/json")
    @TokenCheck
    public Result deleteOneDocumentPhsically(@PathVariable("docId") Integer docId) {
        documentService.deleteOneDocumentPhysically(docId);
        return new CustomExceptionHandler<>().sendSuccessResponse(docId);
    }

    /**
     * 创建者恢复一个文档
     * @param docId
     * @return Result
     */
    @Transactional
    @Modifying
    @PostMapping(path = "/{docId}/creators", produces = "application/json")
    @TokenCheck
    public Result renewOneDocument(@PathVariable("docId") Integer docId) {
        documentService.renewOneDocument(docId);
        return new CustomExceptionHandler<>().sendSuccessResponse(docId);
    }

    /**
     * 修改文档的标题
     * @param docId
     * @return Result
     */
    @Transactional
    @Modifying
    @PutMapping(path = "/{docId}/name", produces = "application/json")
    @TokenCheck
    public Result alterOneDocument(@RequestBody @Validated({DataGroupB.class}) DocumentData documentData,
                                   @PathVariable("docId") Integer docId) {
        // DocumentData documentData = JSON.parseObject(JSON.toJSONString(request.getData()), DocumentData.class);
        documentService.alterOneDocument(docId, documentData.getDocName());
        return new CustomExceptionHandler<>().sendSuccessResponse(docId);
    }

    /**
     * 加入编辑
     * @param docId
     * @return Result
     */
    @Transactional
    @Modifying
    @PostMapping(path = "/{docId}/editors", produces = "application/json")
    @TokenCheck
    public Result enterEditDocument(@PathVariable("docId") Integer docId, HttpServletRequest request) {
        int userId = (int) request.getAttribute(AttributeName.CLIENT_ID);
        documentService.addEditorDocument(docId, userId);
        return new CustomExceptionHandler<>().sendSuccessResponse(docId);
    }

    /**
     * 退出编辑（加入回收站）
     * @param docId
     * @return Result
     */
    @Transactional
    @Modifying
    @DeleteMapping(path = "/{docId}/editors", produces = "application/json")
    @TokenCheck
    public Result quitEditDocument(@PathVariable("docId") Integer docId, HttpServletRequest request) {
        int userId = (int) request.getAttribute(AttributeName.CLIENT_ID);
        documentService.deleteEditorDocument(docId, userId);
        return new CustomExceptionHandler<>().sendSuccessResponse(docId);
    }

    /**
     * 恢复编辑（从回收站中取出）
     * @param docId 文档id
     * @param request http请求头，其中包装了token验证时得到的用户id号
     * @return Result
     */
    @Transactional
    @Modifying
    @PutMapping(path = "/{docId}/editors", produces = "application/json")
    @TokenCheck
    public Result renewEditDocument(@PathVariable("docId") Integer docId, HttpServletRequest request) {
        int userId = (int) request.getAttribute(AttributeName.CLIENT_ID);
        documentService.renewEditorDocument(docId, userId);
        return new CustomExceptionHandler<>().sendSuccessResponse(docId);
    }

    /**
     * 根据文档id获取该文档的一系列操作
     * @param docId 文档id
     * @param request http请求头，其中包装了token验证时得到的userId和账号userNum
     * @return Result
     */
    @GetMapping(path = "/{docId}/operations", produces = "application/json")
    @TokenCheck
    public Result getDocumentOperationData(@PathVariable("docId") Integer docId,
                                           HttpServletRequest request) {
        int userId = (int) request.getAttribute(AttributeName.CLIENT_ID);
        String userNum = (String) request.getAttribute(AttributeName.CLIENT_NUM);

        JSONObject object = new JSONObject();
        TextManagement textManagement = EditorSocketManager.join(docId);
        if(textManagement != null) {
            List<JSONObject> changesListJSON = ToJSON.fromChangesListToJSON(userNum, textManagement.getText().getAllChangesList());

            object.put("docId", docId);
            object.put("docRevision", textManagement.getText().getRevision());
            object.fluentPut("changesList", changesListJSON);

            EditorSocketManager.quit(docId);
        } else {
            object.put("docId", docId);
            object.put("docRevision", 0);
            object.fluentPut("changesList", null);
        }
        // 更新文档的操作时间
        documentService.updateOperationTime(docId, userId);
        return new CustomExceptionHandler<>().sendSuccessResponse(object);
    }

    /**
     * 根据文档id获取该文档的文本内容
     * @param docId 文档id
     * @param request http请求头，其中包装了token验证时得到的用户id号
     * @return Result
     */
    @GetMapping(path = "/{docId}/text", produces = "application/json")
    @TokenCheck
    public Result getDocumentTextData(@PathVariable("docId") Integer docId,
                                           HttpServletRequest request) {
        int userId = (int) request.getAttribute(AttributeName.CLIENT_ID);
        String text = null;
        TextManagement textManagement = EditorSocketManager.join(docId);
        if (textManagement != null) {
            text = textManagement.getText().build();
            EditorSocketManager.quit(docId);
        }
        // 更新文档的操作时间
        documentService.updateOperationTime(docId, userId);
        return new CustomExceptionHandler<>().sendSuccessResponse(text);
    }
}